diff --git a/docs/lab/handout/build-class.py b/docs/lab/handout/build-class.py new file mode 100755 index 0000000..b4db1ee --- /dev/null +++ b/docs/lab/handout/build-class.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +"""One exercise sheet per student, with their prefix filled in, in one PDF. + + python3 build-class.py 100 # students 1..100 -> class.pdf + python3 build-class.py 100 -o out.pdf + python3 build-class.py 5 12 -o few.pdf # students 5..12 + +Student n gets 2a0d:d741:::/48; the lab /40 allows 1..255. Each +sheet is compiled separately and must come out to exactly two pages, so +the result prints double-sided with one student per leaf. Needs pdflatex +and pdfunite (poppler). +""" +import argparse +import os +import shutil +import subprocess +import sys +import tempfile + +HERE = os.path.dirname(os.path.abspath(__file__)) + + +def page_count(pdf): + out = subprocess.run(["pdfinfo", pdf], capture_output=True, text=True, check=True).stdout + return int(next(line.split()[-1] for line in out.splitlines() if line.startswith("Pages:"))) + + +def main(): + ap = argparse.ArgumentParser(description=__doc__.split("\n")[0]) + ap.add_argument("first", type=int, help="first student number, or the count if it is the only number") + ap.add_argument("last", type=int, nargs="?") + ap.add_argument("-o", "--output", default=os.path.join(HERE, "class.pdf")) + args = ap.parse_args() + first, last = (1, args.first) if args.last is None else (args.first, args.last) + if not 1 <= first <= last <= 255: + sys.exit("student numbers must be within 1..255: the lab /40 has 255 usable /48s") + + with tempfile.TemporaryDirectory() as tmp: + for name in os.listdir(HERE): + if name.endswith((".tex", ".cls", ".sty", ".bst", ".bib")): + shutil.copy(os.path.join(HERE, name), tmp) + parts = [] + for n in range(first, last + 1): + job = f"student-{n:03d}" + tex = f"\\def\\studentnum{{{n}}}\\def\\studenthex{{{n:x}}}\\input{{handout.tex}}" + for _ in range(2): # twice, for the figure and table references + r = subprocess.run(["pdflatex", "-interaction=nonstopmode", "-halt-on-error", "-jobname", job, tex], + cwd=tmp, capture_output=True, text=True) + if r.returncode != 0: + sys.exit(f"student {n}: pdflatex failed:\n{r.stdout[-2000:]}") + pdf = os.path.join(tmp, job + ".pdf") + pages = page_count(pdf) + if pages != 2: + sys.exit(f"student {n}: sheet is {pages} pages, not 2; double-sided printing would misalign") + parts.append(pdf) + print(f"\rstudent {n}: 2a0d:d741:{n:x}::/48", end="", flush=True) + print() + subprocess.run(["pdfunite", *parts, args.output], check=True) + print(f"wrote {args.output}: students {first}..{last}, {page_count(args.output)} pages") + + +if __name__ == "__main__": + main() diff --git a/docs/lab/handout/handout.pdf b/docs/lab/handout/handout.pdf index 305d2e0..4c25825 100644 Binary files a/docs/lab/handout/handout.pdf and b/docs/lab/handout/handout.pdf differ diff --git a/docs/lab/handout/handout.tex b/docs/lab/handout/handout.tex index d56aa9e..0d4db8f 100644 --- a/docs/lab/handout/handout.tex +++ b/docs/lab/handout/handout.tex @@ -23,7 +23,17 @@ % ---- things to change per class ------------------------------------------- \newcommand{\laburl}{https://bgplab.rayhaan.net/} -\newcommand{\myprefix}{\texttt{2a0d:d741:}\underline{\texttt{\,n\,}}\texttt{::/48}} +% build-class.py compiles one copy per student with \studentnum and +% \studenthex defined (e.g. 26 and 1a); without them this is the blank sheet. +\providecommand{\studentnum}{} +\providecommand{\studenthex}{} +\ifx\studentnum\empty + \newcommand{\myprefix}{\texttt{2a0d:d741:}\underline{\texttt{\,n\,}}\texttt{::/48}} + \newcommand{\nhex}{} +\else + \newcommand{\myprefix}{\texttt{2a0d:d741:\studenthex::/48}} + \newcommand{\nhex}{\studenthex} +\fi % ---- exercise machinery ------------------------------------------------------ \lstset{basicstyle=\ttfamily\small,columns=fullflexible,keepspaces=true, @@ -54,6 +64,7 @@ \maketitle \medskip\noindent +\ifx\studentnum\empty \parbox[t]{0.44\linewidth}{Name:~\dotfill}\hfill \parbox[t]{0.27\linewidth}{Prefix: \myprefix} @@ -62,6 +73,14 @@ Your prefix is \myprefix, where \texttt{n} is your number \emph{in hexadecimal}.\par\noindent Student 10 has \texttt{2a0d:d741:a::/48} and student 26 has \texttt{2a0d:d741:1a::/48}. +\else +\parbox[t]{0.44\linewidth}{Name:~\dotfill}\hfill +\parbox[t]{0.5\linewidth}{Student \studentnum\quad Prefix: \myprefix} + +\smallskip\noindent +Your prefix is \myprefix: student number \studentnum, written in +hexadecimal. Make sure to use this prefix in the exercise. +\fi % ============================================================================== \section{Who Announces the Address You Are Using?} @@ -76,8 +95,8 @@ announces that prefix. The RIPE database records who holds address space (\texttt{inet6num}, or \texttt{inetnum} for IPv4) and who may announce it (\texttt{route6}, \texttt{route}). Look up your IPv6 address, then your lab prefix: -\begin{lstlisting} -whois whois 2a0d:d741:::/48 +\begin{lstlisting}[escapeinside={(*}{*)}] +whois whois 2a0d:d741:(*\nhex*)::/48 \end{lstlisting} \fillintwo{Your IPv6 address: held by (\texttt{netname})}{\texttt{origin} AS:} \fillintwo{Your lab prefix: \texttt{origin} AS}{is it announced right now?} @@ -116,7 +135,7 @@ Click \emph{bytes} next to your route and hover over the coloured segments. \fillintwo{AS number(s) in \texttt{AS\_PATH}:}{attribute carrying your prefix:} \task{See it from outside.} -Wait a moment, then look your prefix up on \texttt{bgp.tools}. +Wait a moment, then look your prefix up on \texttt{https://bgp.tools/super-lg}. \fillintwo{Origin AS the world sees:}{an AS path you found:} \noindent The lab daemon's AS4200000001 is missing from that path. Where did it go, and why? \answer[2] @@ -124,7 +143,7 @@ Wait a moment, then look your prefix up on \texttt{bgp.tools}. \task{Reach it.} The router answers for every address in the lab space, so your prefix is reachable exactly when your announcement got out:\quad -\texttt{ping -6 2a0d:d741:::1} +\texttt{ping -6 2a0d:d741:\nhex::1} \reach % ============================================================================== @@ -154,7 +173,7 @@ Announce your prefix again with \texttt{210036:0:0} and \texttt{210036:1:42476}; announcing a prefix you own replaces it. Before you look: who do you expect to still see it? \answer[2] -\noindent Wait a moment, then check on \texttt{bgp.tools} and with \texttt{ping}. +\noindent Wait a moment, then check on \texttt{https://bgp.tools/super-lg} and with \texttt{ping}. \reach \task{To SWITCH only.} @@ -162,7 +181,7 @@ Add \texttt{42476:0:0} and \texttt{42476:1:559} to the two you already have and announce again. Our router now tells only the route servers, and they tell only SWITCH. \reach -\noindent Is your prefix still visible on \texttt{bgp.tools}? Explain what you see. +\noindent Is your prefix still visible on \texttt{https://bgp.tools/super-lg}? Explain what you see. \answer[2] \task{Use of large communities.}