Advertisement
devinteske

jng 1.4.1

Oct 27th, 2022
1,337
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 16.35 KB | None | 0 0
  1. #!/bin/sh
  2. #-
  3. # Copyright (c) 2016-2022 Devin Teske <dteske@FreeBSD.org>
  4. # All rights reserved.
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions
  8. # are met:
  9. # 1. Redistributions of source code must retain the above copyright
  10. #    notice, this list of conditions and the following disclaimer.
  11. # 2. Redistributions in binary form must reproduce the above copyright
  12. #    notice, this list of conditions and the following disclaimer in the
  13. #    documentation and/or other materials provided with the distribution.
  14. #
  15. # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  16. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18. # ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  19. # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  21. # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  22. # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  23. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  24. # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  25. # SUCH DAMAGE.
  26. #
  27. # $FreeBSD$
  28. #
  29. ############################################################ IDENT(1)
  30. #
  31. # $Title: netgraph(4) management script for vnet jails $
  32. # $Version: 1.4.1 $
  33. #
  34. ############################################################ INFORMATION
  35. #
  36. # Use this tool with jail.conf(5) (or rc.conf(5) ``legacy'' configuration) to
  37. # manage `vnet' interfaces for jails. Designed to automate the creation of vnet
  38. # interface(s) during jail `prestart' and destroy said interface(s) during jail
  39. # `poststop'.
  40. #
  41. # In jail.conf(5) format:
  42. #
  43. # ### BEGIN EXCERPT ###
  44. #
  45. # xxx {
  46. #   host.hostname = "xxx.yyy";
  47. #   path = "/vm/xxx";
  48. #
  49. #   #
  50. #   # NB: Below 2-lines required
  51. #   # NB: The number of ngN_xxx interfaces should match the number of
  52. #   #     arguments given to `jng bridge xxx' in exec.prestart value.
  53. #   #
  54. #   vnet;
  55. #   vnet.interface = ng0_xxx, ng1_xxx, ...;
  56. #
  57. #   exec.clean;
  58. #   exec.system_user = "root";
  59. #   exec.jail_user = "root";
  60. #
  61. #   #
  62. #   # NB: Below 2-lines required
  63. #   # NB: The number of arguments after `jng bridge xxx' should match
  64. #   #     the number of ngN_xxx arguments in vnet.interface value.
  65. #   #
  66. #   exec.prestart += "jng bridge xxx em0 em1 ...";
  67. #   exec.poststop += "jng shutdown xxx";
  68. #
  69. #   # Standard recipe
  70. #   exec.start += "/bin/sh /etc/rc";
  71. #   exec.stop = "/bin/sh /etc/rc.shutdown jail";
  72. #   exec.consolelog = "/var/log/jail_xxx_console.log";
  73. #   mount.devfs;
  74. #
  75. #   # Optional (default off)
  76. #   #allow.mount;
  77. #   #allow.set_hostname = 1;
  78. #   #allow.sysvipc = 1;
  79. #   #devfs_ruleset = "11"; # rule to unhide bpf for DHCP
  80. # }
  81. #
  82. # ### END EXCERPT ###
  83. #
  84. # In rc.conf(5) ``legacy'' format (used when /etc/jail.conf does not exist):
  85. #
  86. # ### BEGIN EXCERPT ###
  87. #
  88. # jail_enable="YES"
  89. # jail_list="xxx"
  90. #
  91. # #
  92. # # Global presets for all jails
  93. # #
  94. # jail_devfs_enable="YES"   # mount devfs
  95. #
  96. # #
  97. # # Global options (default off)
  98. # #
  99. # #jail_mount_enable="YES"      # mount /etc/fstab.{name}
  100. # #jail_set_hostname_allow="YES"    # Allow hostname to change
  101. # #jail_sysvipc_allow="YES"     # Allow SysV Interprocess Comm.
  102. #
  103. # # xxx
  104. # jail_xxx_hostname="xxx.shxd.cx"       # hostname
  105. # jail_xxx_rootdir="/vm/xxx"            # root directory
  106. # jail_xxx_vnet_interfaces="ng0_xxx ng1xxx ..." # vnet interface(s)
  107. # jail_xxx_exec_prestart0="jng bridge xxx em0 em1 ..."  # bridge interface(s)
  108. # jail_xxx_exec_poststop0="jng shutdown xxx"    # destroy interface(s)
  109. # #jail_xxx_mount_enable="YES"          # mount /etc/fstab.xxx
  110. # #jail_xxx_devfs_ruleset="11"          # rule to unhide bpf for DHCP
  111. #
  112. # ### END EXCERPT ###
  113. #
  114. # Note that the legacy rc.conf(5) format is converted to
  115. # /var/run/jail.{name}.conf by /etc/rc.d/jail if jail.conf(5) is missing.
  116. #
  117. # ASIDE: dhclient(8) inside a vnet jail...
  118. #
  119. # To allow dhclient(8) to work inside a vnet jail, make sure the following
  120. # appears in /etc/devfs.rules (which should be created if it doesn't exist):
  121. #
  122. #   [devfsrules_jail=11]
  123. #   add include $devfsrules_hide_all
  124. #   add include $devfsrules_unhide_basic
  125. #   add include $devfsrules_unhide_login
  126. #   add path 'bpf*' unhide
  127. #
  128. # And set ether devfs.ruleset="11" (jail.conf(5)) or
  129. # jail_{name}_devfs_ruleset="11" (rc.conf(5)).
  130. #
  131. # NB: While this tool can't create every type of desirable topology, it should
  132. # handle most setups, minus some which considered exotic or purpose-built.
  133. #
  134. ############################################################ CONFIGURATION
  135.  
  136. #
  137. # Netgraph node type. Can be `iface' or `eiface' and refers to whether
  138. # ng_iface(4) or ng_eiface(4) is used with ng_bridge(4). The advantages of
  139. # choosing iface over eiface is that with iface you can utilize ng_tcpmss(4)
  140. # to limit the TCP MSS for operating in environments that clamp down on ICMP.
  141. #
  142. # NB: iface/tcpmss support is EXPERIMENTAL
  143. #
  144. NG_TYPE=eiface # Can be iface or eiface
  145.  
  146. #
  147. # Clamp TCP Maximum Segment Size to reasonably below standard MTU
  148. # NB: Fixes TCP hangup issue in environments where ICMP is restricted
  149. # NB: Be liberal about MSS (RFC 879, section 7)
  150. #
  151. NG_TCPMSS_CONFIG='{ inHook="bridge" outHook="'$NG_TYPE'" maxMSS=1280 }'
  152.  
  153. ############################################################ GLOBALS
  154.  
  155. pgm="${0##*/}" # Program basename
  156.  
  157. #
  158. # Global exit status
  159. #
  160. SUCCESS=0
  161. FAILURE=1
  162.  
  163. ############################################################ FUNCTIONS
  164.  
  165. usage()
  166. {
  167.     local action usage descr
  168.     exec >&2
  169.     echo "Usage: $pgm action [arguments]"
  170.     echo "Actions:"
  171.     for action in \
  172.         bridge      \
  173.         graph       \
  174.         show        \
  175.         show1       \
  176.         shutdown    \
  177.         stats       \
  178.     ; do
  179.         eval usage=\"\$jng_${action}_usage\"
  180.         [ "$usage" ] || continue
  181.         eval descr=\"\$jng_${action}_descr\"
  182.         printf "\t%s\n\t\t%s\n" "$usage" "$descr"
  183.     done
  184.     exit $FAILURE
  185. }
  186.  
  187. action_usage()
  188. {
  189.     local usage descr action="$1"
  190.     eval usage=\"\$jng_${action}_usage\"
  191.     echo "Usage: $pgm $usage" >&2
  192.     eval descr=\"\$jng_${action}_descr\"
  193.     printf "\t%s\n" "$descr"
  194.     exit $FAILURE
  195. }
  196.  
  197. derive_mac()
  198. {
  199.     local OPTIND=1 OPTARG __flag
  200.     local __mac_num= __make_pair=
  201.     while getopts 2n: __flag; do
  202.         case "$__flag" in
  203.         2) __make_pair=1 ;;
  204.         n) __mac_num=${OPTARG%%[^0-9]*} ;;
  205.         esac
  206.     done
  207.     shift $(( $OPTIND - 1 ))
  208.  
  209.     if [ ! "$__mac_num" ]; then
  210.         eval __mac_num=\${_${iface}_num:--1}
  211.         __mac_num=$(( $__mac_num + 1 ))
  212.         eval _${iface}_num=\$__mac_num
  213.     fi
  214.  
  215.     local __iface="$1" __name="$2" __var_to_set="$3" __var_to_set_b="$4"
  216.     local __iface_devid __new_devid __num __new_devid_b
  217.     #
  218.     # Calculate MAC address derived from given iface.
  219.     #
  220.     # The formula I'm using is ``NP:SS:SS:II:II:II'' where:
  221.     # + N denotes 4 bits used as a counter to support branching
  222.     #   each parent interface up to 15 times under the same jail
  223.     #   name (see S below).
  224.     # + P denotes the special nibble whose value, if one of
  225.     #   2, 6, A, or E (but usually 2) denotes a privately
  226.     #   administered MAC address (while remaining routable).
  227.     # + S denotes 16 bits, the sum(1) value of the jail name.
  228.     # + I denotes bits that are inherited from parent interface.
  229.     #
  230.     # The S bits are a CRC-16 checksum of NAME, allowing the jail
  231.     # to change link numbers in ng_bridge(4) without affecting the
  232.     # MAC address. Meanwhile, if...
  233.     #   + the jail NAME changes (e.g., it was duplicated and given
  234.     #     a new name with no other changes)
  235.     #   + the underlying network interface changes
  236.     #   + the jail is moved to another host
  237.     # the MAC address will be recalculated to a new, similarly
  238.     # unique value preventing conflict.
  239.     #
  240.     __iface_devid=$( ifconfig $__iface ether | awk '/ether/,$0=$2' )
  241.     # ??:??:??:II:II:II
  242.     __new_devid=${__iface_devid#??:??:??} # => :II:II:II
  243.     # => :SS:SS:II:II:II
  244.     __num=$( set -- `echo -n "$__name" | sum` && echo $1 )
  245.     __new_devid=$( printf :%02x:%02x \
  246.         $(( $__num >> 8 & 255 )) $(( $__num & 255 )) )$__new_devid
  247.     # => P:SS:SS:II:II:II
  248.     case "$__iface_devid" in
  249.        ?2:*) __new_devid=a$__new_devid __new_devid_b=e$__new_devid ;;
  250.     ?[Ee]:*) __new_devid=2$__new_devid __new_devid_b=6$__new_devid ;;
  251.           *) __new_devid=2$__new_devid __new_devid_b=e$__new_devid
  252.     esac
  253.     # => NP:SS:SS:II:II:II
  254.     __new_devid=$( printf %x $(( $__mac_num & 15 )) )$__new_devid
  255.     __new_devid_b=$( printf %x $(( $__mac_num & 15 )) )$__new_devid_b
  256.  
  257.     #
  258.     # Return derivative MAC address(es)
  259.     #
  260.     if [ "$__make_pair" ]; then
  261.         if [ "$__var_to_set" -a "$__var_to_set_b" ]; then
  262.             eval $__var_to_set=\$__new_devid
  263.             eval $__var_to_set_b=\$__new_devid_b
  264.         else
  265.             echo $__new_devid $__new_devid_b
  266.         fi
  267.     else
  268.         if [ "$__var_to_set" ]; then
  269.             eval $__var_to_set=\$__new_devid
  270.         else
  271.             echo $__new_devid
  272.         fi
  273.     fi
  274. }
  275.  
  276. mustberoot_to_continue()
  277. {
  278.     if [ "$( id -u )" -ne 0 ]; then
  279.         echo "Must run as root!" >&2
  280.         exit $FAILURE
  281.     fi
  282. }
  283.  
  284. jng_bridge_usage="bridge [-b BRIDGE_NAME] NAME [!|=]iface0 [[!|=]iface1 ...]"
  285. jng_bridge_descr="Create ng0_NAME [ng1_NAME ...]"
  286. jng_bridge()
  287. {
  288.     local OPTIND=1 OPTARG flag bridge=bridge
  289.     while getopts b: flag; do
  290.         case "$flag" in
  291.         b) bridge="$OPTARG"
  292.            [ "$bridge" ] || action_usage bridge ;; # NOTREACHED
  293.         *) action_usage bridge # NOTREACHED
  294.         esac
  295.     done
  296.     shift $(( $OPTIND - 1 ))
  297.  
  298.     local name="$1"
  299.     [ "${name:-x}" = "${name#*[!0-9a-zA-Z_]}" -a $# -gt 1 ] ||
  300.         action_usage bridge # NOTREACHED
  301.     shift 1 # name
  302.  
  303.     mustberoot_to_continue
  304.  
  305.     local iface parent jiface jiface_devid
  306.     local new clone_mac no_derive num quad i=0
  307.     for iface in $*; do
  308.  
  309.         clone_mac=
  310.         no_derive=
  311.         case "$iface" in
  312.         =*) iface=${iface#=} clone_mac=1 ;;
  313.         !*) iface=${iface#!} no_derive=1 ;;
  314.         esac
  315.  
  316.         # Make sure the interface doesn't exist already
  317.         jiface=ng${i}_$name
  318.         if ngctl msg "$jiface:" getifname > /dev/null 2>&1; then
  319.             i=$(( $i + 1 ))
  320.             continue
  321.         fi
  322.  
  323.         # Bring the interface up
  324.         ifconfig $iface up || return
  325.  
  326.         # Set promiscuous mode and don't overwrite src addr
  327.         ngctl msg $iface: setpromisc 1 || return
  328.         ngctl msg $iface: setautosrc 0 || return
  329.  
  330.         # Make sure the interface has been bridged
  331.         if ! ngctl info ${iface}bridge: > /dev/null 2>&1; then
  332.             ngctl mkpeer $iface: bridge lower link0 || return
  333.             ngctl connect $iface: $iface:lower upper link1 ||
  334.                 return
  335.             ngctl name $iface:lower ${iface}bridge || return
  336.         fi
  337.  
  338.         # Optionally create a secondary bridge
  339.         if [ "$bridge" != "bridge" ] &&
  340.            ! ngctl info "$iface$bridge:" > /dev/null 2>&1
  341.         then
  342.             num=2
  343.             while ngctl msg ${iface}bridge: getstats $num \
  344.                 > /dev/null 2>&1
  345.             do
  346.                 num=$(( $num + 1 ))
  347.             done
  348.             ngctl mkpeer $iface:lower bridge link$num link1 ||
  349.                 return
  350.             ngctl name ${iface}bridge:link$num "$iface$bridge" ||
  351.                 return
  352.         fi
  353.  
  354.         # Create a new interface to the bridge
  355.         num=2
  356.         while ngctl msg "$iface$bridge:" getstats $num > /dev/null 2>&1
  357.         do
  358.             num=$(( $num + 1 ))
  359.         done
  360.         local hook peerhook
  361.         case "$NG_TYPE" in
  362.         eiface)
  363.             # Hook the eiface directly to the bridge
  364.             hook=link$num peerhook=ether
  365.             ngctl mkpeer "$iface$bridge:" \
  366.                 $NG_TYPE $hook $peerhook || return
  367.             ;;
  368.         iface)
  369.             # Hook tcpmss<->iface to bridge
  370.             hook=link$num peerhook=bridge
  371.             ngctl mkpeer "$iface$bridge:" \
  372.                 tcpmss $hook $peerhook || return
  373.             hook=iface peerhook=inet
  374.             ngctl mkpeer "$iface$bridge:link$num" \
  375.                 $NG_TYPE $hook $peerhook || return
  376.             ;;
  377.         *) return $FAILURE
  378.         esac
  379.  
  380.         # Rename the new interface
  381.         while [ ${#jiface} -gt 15 ]; do # OS limitation
  382.             jiface=${jiface%?}
  383.         done
  384.         case "$NG_TYPE" in
  385.         eiface)
  386.             new=$( ngctl show -n "$iface$bridge:link$num" ) ||
  387.                 return
  388.             new=$( set -- $new; echo $2 )
  389.             ngctl name "$iface$bridge:link$num" $jiface || return
  390.             ;;
  391.         iface)
  392.             ngctl name "$iface$bridge:link$num" $jiface-mss ||
  393.                 return
  394.             new=$( ngctl show -n "$jiface-mss:$hook" ) || return
  395.             new=$( set -- $new; echo $2 )
  396.             ngctl name $jiface-mss:$hook $jiface || return
  397.             ngctl msg $jiface: broadcast || return
  398.             ngctl msg $jiface-mss: config "$NG_TCPMSS_CONFIG" ||
  399.                 return
  400.             ;;
  401.         esac
  402.         ifconfig $new name $jiface || return
  403.         ifconfig $jiface up || return
  404.  
  405.         #
  406.         # Set the MAC address of the new interface using a sensible
  407.         # algorithm to prevent conflicts on the network.
  408.         #
  409.         jiface_devid=
  410.         if [ "$clone_mac" ]; then
  411.             jiface_devid=$( ifconfig $iface ether |
  412.                 awk '/ether/,$0=$2' )
  413.         elif [ ! "$no_derive" ]; then
  414.             derive_mac $iface "$name" jiface_devid
  415.         fi
  416.         [ "$jiface_devid" ] &&
  417.             ifconfig $jiface ether $jiface_devid > /dev/null 2>&1
  418.  
  419.         i=$(( $i + 1 ))
  420.     done # for iface
  421. }
  422.  
  423. jng_graph_usage="graph [-f] [-T type] [-o output]"
  424. jng_graph_descr="Generate network graph (default output is \`jng.svg')"
  425. jng_graph()
  426. {
  427.     local OPTIND=1 OPTARG flag
  428.     local output=jng.svg output_type= force=
  429.     while getopts fo:T: flag; do
  430.         case "$flag" in
  431.         f) force=1 ;;
  432.         o) output="$OPTARG" ;;
  433.         T) output_type="$OPTARG" ;;
  434.         *) action_usage graph # NOTREACHED
  435.         esac
  436.     done
  437.     shift $(( $OPTIND - 1 ))
  438.     [ $# -eq 0 -a "$output" ] || action_usage graph # NOTREACHED
  439.     mustberoot_to_continue
  440.     if [ -e "$output" -a ! "$force" ]; then
  441.         echo "$output: Already exists (use \`-f' to overwrite)" >&2
  442.         return $FAILURE
  443.     fi
  444.     if [ ! "$output_type" ]; then
  445.         local valid suffix
  446.         valid=$( dot -Txxx 2>&1 )
  447.         for suffix in ${valid##*:}; do
  448.             [ "$output" != "${output%.$suffix}" ] || continue
  449.             output_type=$suffix
  450.             break
  451.         done
  452.     fi
  453.     ngctl dot | dot ${output_type:+-T "$output_type"} -o "$output"
  454. }
  455.  
  456. jng_show_usage="show"
  457. jng_show_descr="List possible NAME values for \`show NAME'"
  458. jng_show1_usage="show NAME"
  459. jng_show1_descr="Lists ng0_NAME [ng1_NAME ...]"
  460. jng_show2_usage="show [NAME]"
  461. jng_show()
  462. {
  463.     local OPTIND=1 OPTARG flag
  464.     while getopts "" flag; do
  465.         case "$flag" in
  466.         *) action_usage show2 # NOTREACHED
  467.         esac
  468.     done
  469.     shift $(( $OPTIND - 1 ))
  470.     mustberoot_to_continue
  471.     if [ $# -eq 0 ]; then
  472.         ngctl ls | awk '$4=="bridge",$0=$2' |
  473.             xargs -rn1 -Ibridge ngctl show bridge: |
  474.             awk 'sub(/^ng[[:digit:]]+_/, "", $2), $0 = $2' |
  475.             sort -u
  476.         return
  477.     fi
  478.     ngctl ls | awk -v name="$1" '
  479.         BEGIN { N = length(name) + 1 }
  480.         !match(ng = $2, /^ng[[:digit:]]+_/) { next }
  481.         { _name = substr(ng, S = RSTART + RLENGTH) }
  482.         _name != name && substr(_name, 1, N) != name "-" { next }
  483.         (type = $4) ~ /^(e?iface|tcpmss)$/, $0 = ng
  484.     ' | sort
  485. }
  486.  
  487. jng_shutdown_usage="shutdown NAME"
  488. jng_shutdown_descr="Shutdown ng0_NAME [ng1_NAME ...]"
  489. jng_shutdown()
  490. {
  491.     local OPTIND=1 OPTARG flag
  492.     while getopts "" flag; do
  493.         case "$flag" in
  494.         *) action_usage shutdown # NOTREACHED
  495.         esac
  496.     done
  497.     shift $(( $OPTIND -1 ))
  498.     local name="$1"
  499.     [ "${name:-x}" = "${name#*[!0-9a-zA-Z_]}" -a $# -eq 1 ] ||
  500.         action_usage shutdown # NOTREACHED
  501.     mustberoot_to_continue
  502.     jng_show "$name" | xargs -rn1 -I jiface ngctl shutdown jiface:
  503. }
  504.  
  505. jng_stats_usage="stats NAME"
  506. jng_stats_descr="Show ng_bridge link statistics for NAME interfaces"
  507. jng_stats()
  508. {
  509.     local OPTIND=1 OPTARG flag
  510.     while getopts "" flag; do
  511.         case "$flag" in
  512.         *) action_usage stats # NOTREACHED
  513.         esac
  514.     done
  515.     shift $(( $OPTIND -1 ))
  516.     local name="$1"
  517.     [ "${name:-x}" = "${name#*[!0-9a-zA-Z_]}" -a $# -eq 1 ] ||
  518.         action_usage stats # NOTREACHED
  519.     mustberoot_to_continue
  520.     if ifconfig -l | xargs -n1 | fgrep -qw "$name"; then
  521.         { ngctl msg ${name}bridge: getstats 0 # lower link0
  522.           ngctl msg ${name}bridge: getstats 1 # upper link1
  523.         } | awk 1
  524.     fi
  525.     local jiface
  526.     for jiface in $( jng_show "$name" ); do
  527.         echo "$jiface:"
  528.         ngctl show $jiface: | awk '
  529.         $3 == "bridge" && $5 ~ /^link/ {
  530.             bridge = $2
  531.             link = substr($5, 5)
  532.             system(sprintf("ngctl msg %s: getstats %u",
  533.                 bridge, link))
  534.         }' | fmt 2 | awk '
  535.             /=/ && fl = index($0, "=") {
  536.                 printf "%20s = %s\n",
  537.                     substr($0, 0, fl-1),
  538.                     substr($0, 0, fl+1)
  539.             }
  540.         ' # END-QUOTE
  541.     done
  542. }
  543.  
  544. ############################################################ MAIN
  545.  
  546. #
  547. # Command-line arguments
  548. #
  549. action="$1"
  550. [ "$action" ] || usage # NOTREACHED
  551.  
  552. #
  553. # Validate action argument
  554. #
  555. if [ "$BASH_VERSION" ]; then
  556.     type="$( type -t "jng_$action" )" || usage # NOTREACHED
  557. else
  558.     type="$( type "jng_$action" 2> /dev/null )" || usage # NOTREACHED
  559. fi
  560. case "$type" in
  561. *function)
  562.     shift 1 # action
  563.     eval "jng_$action" \"\$@\"
  564.     ;;
  565. *) usage # NOTREACHED
  566. esac
  567.  
  568. ################################################################################
  569. # END
  570. ################################################################################
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement