├── README.md └── wiconfig /README.md: -------------------------------------------------------------------------------- 1 | #wiconfig 2 | Simplified wifi on OpenBSD 3 | 4 | ##EXAMPLE 5 | 6 | Manually configure a wireless interface 7 | 8 | `# sh /etc/wiconfig iwi0` 9 | 10 | Automatically scan for wireless networks and, using previous 11 | manual 12 | configurations, configure the wireless interface based on the 13 | strongest 14 | wireless signal (for use with hostname.if(5) files) 15 | 16 | ``` 17 | $ cat /etc/hostname.iwi0 18 | !/bin/sh /etc/wiconfig -q \$if 19 | ``` 20 | 21 | With the above /etc/hostname.iwi0 in place, iwi0 will be 22 | configured 23 | upon startup or whenever /etc/netstart iwi0 is invoked. 24 | 25 | wiconfig can also be used in conjunction with apmd. In the 26 | following 27 | example, upon resume, it'll check the status of the wireless 28 | connection 29 | and, if there is no network connection, it'll automatically scan 30 | for 31 | wireless networks and, using previous manual configurations, 32 | configure 33 | the wireless interface based on the strongest wireless signal. 34 | 35 | ``` 36 | $ cat /etc/apmd/resume 37 | #!/bin/sh 38 | /bin/sh /etc/wiconfig -qs iwi0 39 | ``` 40 | -------------------------------------------------------------------------------- /wiconfig: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # v.9.5 2/7/2012 17:30 4 | # 5 | # Copyright (c) 2012 Daniel Melameth 6 | # 7 | # Permission to use, copy, modify and distribute this software for any purpose 8 | # with or without fee is hereby granted, provided that the above copyright 9 | # notice and this permission notice appear in all copies. 10 | # 11 | # This software is provided by the regents and contributors "as is" and any 12 | # express or implied warranties, including, but not limited to, the implied 13 | # warranties of merchantability and fitness for a particular purpose are 14 | # disclaimed. In no event shall the regents or contributors be liable for any 15 | # direct, indirect, incidental, special, exemplary or consequential damages 16 | # (including, but not limited to, procurement of substitute goods or services; 17 | # loss of use, data or profits; or business interruption) however caused and on 18 | # any theory of liability, whether in contract, strict liability or tort 19 | # (including negligence or otherwise) arising in any way out of the use of this 20 | # software even if advised of the possibility of such damage. 21 | # 22 | # NAME 23 | # wiconfig - simplifies the configuration of wireless interfaces 24 | # 25 | # SYNOPSIS 26 | # wiconfig [-dnqs] interface 27 | # 28 | # EXAMPLE 29 | # Manually configure a wireless interface 30 | # 31 | # # sh /etc/wiconfig iwi0 32 | # 33 | # Automatically scan for wireless networks and, using previous manual 34 | # configurations, configure the wireless interface based on the strongest 35 | # wireless signal (for use with hostname.if(5) files) 36 | # 37 | # $ cat /etc/hostname.iwi0 38 | # !/bin/sh /etc/wiconfig -q \$if 39 | # 40 | # With the above /etc/hostname.iwi0 in place, iwi0 will be configured 41 | # upon startup or whenever /etc/netstart iwi0 is invoked. 42 | # 43 | # wiconfig can also be used in conjunction with apmd(8). In the 44 | # following example, upon resume, it'll check the status of the wireless 45 | # connection and, if there is no network connection, it'll automatically 46 | # scan for wireless networks and, using previous manual configurations, 47 | # configure the wireless interface based on the strongest wireless 48 | # signal. 49 | # 50 | # $ cat /etc/apmd/resume 51 | # #!/bin/sh 52 | # /bin/sh /etc/wiconfig -qs iwi0 53 | # 54 | # apmd will need this file to be executable so you'll want to do this as 55 | # well 56 | # 57 | # # chmod 0744 /etc/apm/resume 58 | # 59 | # FILES 60 | # /etc/wiconfig.db Wireless network database 61 | # 62 | # CAVEATS 63 | # 1) Only DHCP is supported 64 | # 2) No user-defined nwid prioritization--the nwid with the strongest 65 | # signal will always be preferred 66 | # 3) Only the first 20 nwids with the strongest signals are used 67 | # 4) When used within a hostname.if(5), host startup will be delayed 68 | # slightly while a wireless network scan is performed 69 | # 5) Database records are never purged--existing entries will be updated, 70 | # but unwanted entries need to be removed manually 71 | # 6) Hidden nwids are not supported 72 | 73 | # set -x 74 | 75 | # Save default IFS 76 | oIFS=$IFS 77 | myname=$0 78 | 79 | max=20 80 | # Number of seconds to wait before checking interface status 81 | seconds=3 82 | wiconfigdb="/etc/wiconfig.db" 83 | 84 | function usage { 85 | echo "usage: $myname [-dnqs] interface" 86 | exit 1 87 | } 88 | 89 | # Determine network status and name 90 | function review { 91 | # Assume we are not connected to a network 92 | typeset _i=1 _status=false _ifconfig _nwid _yn 93 | # We are being called from apmd 94 | $quiet && sleep 2 95 | # Need to use a co-process here to handle _status (and _nwid?) 96 | ifconfig "$if" |& while read -p _ifconfig; do 97 | case $_i in 98 | # Sixth line/status 99 | 6) active $_ifconfig && _status=true;; 100 | # Seventh line/nwid 101 | 7) # Connected to an active network 102 | if $_status; then 103 | set $_ifconfig 104 | _nwid=${3#\"} 105 | # nwid begins with a quote 106 | if [ ${#3} -gt ${#_nwid} ]; then 107 | # nwid is not hidden 108 | if [ ${#3} -gt 2 ]; then 109 | IFS='"' 110 | set $_ifconfig 111 | _nwid="$2" 112 | IFS=$oIFS 113 | else 114 | unset _nwid 115 | fi 116 | fi 117 | fi 118 | break;; 119 | esac 120 | _i=$(($_i+1)) 121 | done 122 | 123 | if $_status; then 124 | if $quiet; then 125 | exit 126 | else 127 | different "$_nwid" 128 | fi 129 | else 130 | start 131 | fi 132 | } 133 | 134 | # Determine if the network is active 135 | function active { 136 | typeset _status=$* 137 | typeset _length=${#_status} 138 | _status=${_status%active} 139 | # Network is active 140 | if [ ${#_status} -lt $_length ]; then 141 | return 142 | fi 143 | return 1 144 | } 145 | 146 | function different { 147 | typeset _yn 148 | echo "Currently connected to $*." 149 | read _yn?"Would you like to connect to a different network (y/n)? " 150 | case $_yn in 151 | y) start;; 152 | n) exit;; 153 | *) different "$*";; 154 | esac 155 | } 156 | 157 | function start { 158 | readdb 159 | scan 160 | createarray 161 | match 162 | # Position of nwid in db 163 | typeset _n=$? 164 | # Automatically configuring interface 165 | if $quiet; then 166 | # Found an nwid match 167 | if [ $_n -ne 0 ]; then 168 | echo "connecting to wireless network ${r[$_n]}" 169 | configure "${r[$_n]}" "${r[$_n+2]}" 170 | else 171 | exit 1 172 | fi 173 | else 174 | if [ $_n -ne 0 ]; then 175 | # Reconnection desired 176 | if $(reconnect $_n); then 177 | configure "${r[$_n]}" "${r[$_n+2]}" 178 | exit 179 | fi 180 | fi 181 | menu 182 | fi 183 | } 184 | 185 | function readdb { 186 | # If db exists and is readable 187 | if [ -r $wiconfigdb ]; then 188 | typeset _i=1 189 | while read r[$_i]; do 190 | _i=$((_i+1)) 191 | done < $wiconfigdb 192 | # Remove newline from array as it's counted in ${#r[@]} 193 | unset r[$_i] 194 | fi 195 | } 196 | 197 | # Parse and sort ifconfig nwid output 198 | function scan { 199 | # Need to include a quote to account for nwids with spaces 200 | # IFS=' "' 201 | # IFS=$oIFS 202 | echo -n > "$output" 203 | typeset _nwids _args _nwid 204 | 205 | ! $quiet && echo "Performing wireless scan..." 206 | # Parse ifconfig nwid output for sorting 207 | ifconfig $if scan | grep ^[[:space:]]*nwid | while read _nwids; do 208 | # nwid name chan channel bssid mac db speed options 209 | # Required to set positional parameters 210 | set $_nwids 211 | _args=$# 212 | # Remove possible leading double quote 213 | _nwid=${2#\"} 214 | # nwid begins with a quote 215 | if [ ${#2} -gt ${#_nwid} ]; then 216 | # nwid is not hidden 217 | if [ ${#2} -gt 2 ]; then 218 | IFS='"' 219 | set $_nwids 220 | _nwid=$2 221 | shift 2 222 | _nwids=$* 223 | IFS=$oIFS 224 | set $_nwids 225 | else 226 | continue 227 | fi 228 | else 229 | shift 2 230 | fi 231 | 232 | # shift 233 | # nwid has one or more spaces 234 | # if [ $_args -gt 9 ]; then 235 | # # Remove possible leading double quote 236 | # _nwid=${1#\"} 237 | # shift 238 | # _args=$(($_args-1)) 239 | # while [ $_args -gt 9 ]; do 240 | # _nwid=$_nwid $1 241 | # # _nwid=$_nwid\ $1 242 | # shift 243 | # _args=$(($_args-1)) 244 | # done 245 | # # Append and remove trailing double quote 246 | # _nwid=$_nwid\ ${1%\"} 247 | # else 248 | # _nwid=$1 249 | # fi 250 | 251 | # shift 252 | # unset _nwid 253 | # nwid might contain one or more spaces 254 | # while [ $_args -ge 9 ]; do 255 | # _nwid="${_nwid:-$1} ${_nwid:+$1}" 256 | # shift 257 | # _args=$(($_args-1)) 258 | # done 259 | 260 | # nwid is hidden? 261 | # [[ X$4 = X00:00:00:00:00:00 ]] && continue 262 | 263 | echo -n "$_nwid" >> $output 264 | # Channel 265 | echo -n "|$2" >> $output 266 | # MAC 267 | echo -n "|$4" >> $output 268 | # Signal quality 269 | printf "|%02d" ${5%dBm} >> $output 270 | # Speed 271 | echo -n "|$6" >> $output 272 | # Options 273 | echo "|$7" >> $output 274 | # echo $name $number $chan $mac $db $speed $options 275 | done 276 | IFS=$oIFS 277 | 278 | # Wireless network(s) found 279 | if [ -s "$output" ]; then 280 | # Sort nwids by greatest signal quality 281 | sort -rk 4 -o "$input" -t "|" "$output" 282 | else 283 | if ! $quiet; then 284 | rescan 285 | else 286 | exit 1 287 | fi 288 | fi 289 | } 290 | 291 | function rescan { 292 | typeset _rq 293 | read _rq?"No wireless networks found. Enter r to rescan or q to quit: " 294 | case $_rq in 295 | r) scan;; 296 | q) exit;; 297 | *) rescan;; 298 | esac 299 | } 300 | 301 | # Create sorted array of top $max nwids 302 | function createarray { 303 | IFS='|' 304 | typeset _i=1 _length 305 | # If the array exists 306 | [[ -n ${index[1]} ]] && \ 307 | unset nwid chan mac db speed options index access 308 | while read nwid[$_i] chan[$_i] mac[$_i] db[$_i] speed[$_i] options[$_i] && [ $_i -le $max ]; do 309 | index[$_i]=$_i 310 | # Determine if access is secure 311 | _length=${#options[$_i]} 312 | options=${options[$_i]#privacy} 313 | # Access is secure 314 | if [ ${#options} -lt $_length ]; then 315 | access[$_i]="Secured" 316 | else 317 | access[$_i]="Unsecured" 318 | fi 319 | _i=$(($_i+1)) 320 | done < "$input" 321 | IFS=$oIFS 322 | } 323 | 324 | # Linear search for best nwid match 325 | function match { 326 | typeset _i _m 327 | # Start with the nwid with the strongest signal 328 | for _i in ${index[@]}; do 329 | matchdb "${nwid[$_i]}" "${mac[$_i]}" 330 | _m=$? 331 | # Match found 332 | [[ $_m -ne 0 ]] && return $_m 333 | done 334 | return 0 335 | } 336 | 337 | # Return match in the db 338 | function matchdb { 339 | # Start with last MAC in db 340 | typeset _i=$((${#r[@]}-1)) 341 | # More records in the db 342 | while [ $_i -gt 0 ]; do 343 | # MAC and nwid matches 344 | if [ "X$2" = "X${r[$_i]}" ] && \ 345 | [ "X$1" = "X${r[$_i-1]}" ]; then 346 | # Return position of nwid in db 347 | return $(($_i-1)) 348 | fi 349 | # Move to previous MAC (and network) in db 350 | _i=$(($_i-3)) 351 | done 352 | } 353 | 354 | # Configure interface 355 | function configure { 356 | ifconfig $if -nwid -nwkey -wpakey down > /dev/null 2>&1 357 | # Apparently we need to use eval and single quotes to handle nwids with 358 | # spaces 359 | eval ifconfig $if nwid \'$1\' $2 up > /dev/null 2>&1 360 | if [ ! $nodhcp ]; then 361 | dhclient $if 362 | fi 363 | } 364 | 365 | function reconnect { 366 | typeset _yn 367 | read _yn?"${r[$1]} found. Would you like to reconnect (y/n)? " 368 | case $_yn in 369 | y) return;; 370 | n) return 1;; 371 | *) reconnect;; 372 | esac 373 | } 374 | 375 | function menu { 376 | typeset _i 377 | echo 378 | printf " %-40s %-6s %-10s\n" "Network Name" "Signal" "Access" 379 | echo 380 | for _i in ${index[@]}; do 381 | printf "%3d) %-40s %-6s %-10s\n" \ 382 | $_i "${nwid[$_i]}" "${db[$_i]}dB" "${access[$_i]}" 383 | done 384 | echo 385 | read choice?"Enter the number of the network to connect to (or r to rescan or q to quit): " 386 | if [ $choice -ge 1 ] && [ $choice -le ${#index[@]} ]; then 387 | if [ "X${access[$choice]}" = XSecured ]; then 388 | password 389 | determine 390 | else 391 | configure "${nwid[$choice]}" 392 | update 393 | fi 394 | elif [ "X$choice" = "Xr" ]; then 395 | start 396 | elif [ "X$choice" = "Xq" ]; then 397 | exit 398 | else 399 | echo "Invalid choice" 400 | sleep 1 401 | menu 402 | fi 403 | } 404 | 405 | function password { 406 | stty -echo 407 | read -r pass1?"Enter the password for ${nwid[$choice]} (will not echo): " 408 | echo 409 | read -r pass2?"Enter the password for ${nwid[$choice]} (again): " 410 | echo 411 | stty echo 412 | # If passwords do not match or are blank 413 | if [ "X$pass1" != "X$pass2" ] || [ "X$pass1" = X ]; then 414 | echo "Passwords do not match or are invalid" 415 | sleep 1 416 | password 417 | fi 418 | } 419 | 420 | # Determine if we are using WPA or WEP 421 | function determine { 422 | echo "Connecting to wireless network ${nwid[$choice]}..." 423 | ifconfig "$if" -nwid -nwkey -wpakey down > /dev/null 2>&1 424 | # Must bring interface up for status to become active 425 | ifconfig "$if" nwid "${nwid[$choice]}" wpakey "$pass1" up > /dev/null 2>&1 426 | typeset _status=$? 427 | # Lackluster workaround for athn taking a while to become active 428 | [[ $if = athn? ]] && seconds=11 429 | sleep $seconds 430 | # Network is active 431 | if [ $_status -eq 0 ] && active $(ifconfig "$if" | fgrep status); then 432 | update wpa 433 | else 434 | ifconfig "$if" -nwid -wpakey down > /dev/null 2>&1 435 | ifconfig "$if" nwid "${nwid[$choice]}" nwkey "$pass1" up > /dev/null 2>&1 436 | _status=$? 437 | sleep $seconds 438 | if [ $_status -eq 0 ] && \ 439 | active $(ifconfig "$if" | fgrep status); then 440 | update wep 441 | else 442 | echo "Unable to connect" 443 | exit 1 444 | fi 445 | fi 446 | if [ ! $nodhcp ]; then 447 | dhclient $if 448 | fi 449 | } 450 | 451 | # Update existing db record, if it exists, or create a new one 452 | function update { 453 | # Number of entries in db 454 | typeset _i=${#r[@]} _m 455 | # db is not empty 456 | if [ $_i -gt 0 ]; then 457 | matchdb "${nwid[$choice]}" "${mac[$choice]}" 458 | _m=$? 459 | # Match found 460 | if [ $_m -ne 0 ]; then 461 | secure $(($_m+2)) $1 462 | createdb 463 | return 464 | fi 465 | fi 466 | r[$_i+1]="${nwid[$choice]}" 467 | r[$_i+2]="${mac[$choice]}" 468 | secure $(($_i+3)) $1 469 | createdb 470 | } 471 | 472 | # Set nwid access parameters for db record 473 | function secure { 474 | case $2 in 475 | wpa) r[$1]="wpakey \"$pass1\"";; 476 | wep) r[$1]="nwkey $pass1";; 477 | # Open nwid 478 | *) r[$1]="";; 479 | esac 480 | } 481 | 482 | function createdb { 483 | # If the db does not exist, create and secure it 484 | if [ ! -a "$wiconfigdb" ]; then 485 | touch "$wiconfigdb" 486 | chmod 640 "$wiconfigdb" 487 | fi 488 | 489 | echo -n > "$wiconfigdb" 490 | typeset _i=1 491 | while [ $_i -le ${#r[@]} ]; do 492 | echo "${r[$_i]}" >> "$wiconfigdb" 493 | _i=$(($_i+1)) 494 | done 495 | } 496 | 497 | function end { 498 | rm -f "$output" "$input" 499 | } 500 | 501 | trap end EXIT ERR INT KILL TERM 502 | 503 | # Debugging for functions (must be specified after the function declaration) 504 | # typeset -ft review 505 | # typeset -ft active 506 | # typeset -ft different 507 | # typeset -ft start 508 | # typeset -ft readdb 509 | # typeset -ft scan 510 | # typeset -ft rescan 511 | # typeset -ft createarray 512 | # typeset -ft match 513 | # typeset -ft matchdb 514 | # typeset -ft configure 515 | # typeset -ft reconnect 516 | # typeset -ft menu 517 | # typeset -ft password 518 | # typeset -ft determine 519 | # typeset -ft update 520 | # typeset -ft secure 521 | # typeset -ft createdb 522 | 523 | debug=false 524 | # Assume we are being used interactively 525 | quiet=false 526 | # Do not check the wireless network status before configuring the interface 527 | # (expected in the hostname.if(5) case) 528 | status=false 529 | 530 | if [ "X$(whoami)" != Xroot ]; then 531 | echo "$myname must be run as root" 532 | exit 1 533 | fi 534 | 535 | while getopts dnqs opt; do 536 | case $opt in 537 | d) debug=true;; 538 | n) nodhcp=true;; 539 | q) quiet=true;; 540 | s) status=true;; 541 | ?) usage;; 542 | esac 543 | done 544 | 545 | if $debug; then 546 | set -x 547 | typeset -ft review active different start readdb \ 548 | scan rescan createarray match matchdb configure reconnect menu \ 549 | password determine update secure createdb 550 | fi 551 | 552 | shift $(($OPTIND-1)) 553 | 554 | # No interface specified 555 | [[ -z "$1" ]] && usage 556 | 557 | if="$1" 558 | ifconfig "$if" > /dev/null 2>&1 559 | 560 | # Interface does not exist 561 | if [ $? -ne 0 ]; then 562 | # Manually configuring interface 563 | if ! $quiet; then 564 | echo "Interface $if does not exist" 565 | fi 566 | exit 1 567 | fi 568 | 569 | output=$(mktemp) 570 | input=$(mktemp) 571 | 572 | # Running from hostname.if 573 | if $quiet && ! $status; then 574 | start 575 | else 576 | review 577 | fi 578 | --------------------------------------------------------------------------------