diff --git a/.gitignore b/.gitignore
index e72a092..2d9846f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@
# Ignore log files
makeBook.log
indexBook.log
+RFCbib6.log
diff --git a/20. Further Reading/RFC bibliography.md b/20. Further Reading/RFC bibliography.md
index bec9bff..ce31eb4 100644
--- a/20. Further Reading/RFC bibliography.md
+++ b/20. Further Reading/RFC bibliography.md
@@ -1,6 +1,10 @@
## RFC bibliography
-This section is a machine-generated list of all current RFCs that mention IPv6 in their title. Obsolete RFCs are not included. Be *cautious* about old Informational or Experimental RFCs - they may be misleading as well as out of date.
+This section is a machine-generated list of all current RFCs that mention
+IPv6 in their title. Obsolete RFCs are not included. Be *cautious* about old
+Informational or Experimental RFCs - they may be misleading as well as out of date.
+
+RFCbib6 run at 2023-05-21 14:22:57 UTC+1200
__Standards Track__
@@ -208,7 +212,6 @@ __Standards Track__
- [RFC9352](https://www.rfc-editor.org/info/rfc9352): IS-IS Extensions to Support Segment Routing over the IPv6 Data Plane
- [RFC9354](https://www.rfc-editor.org/info/rfc9354): Transmission of IPv6 Packets over Power Line Communication (PLC) Networks
-
__Best Current Practice__
- [RFC3901](https://www.rfc-editor.org/info/rfc3901): DNS IPv6 Transport Operational Guidelines
@@ -407,6 +410,5 @@ __Experimental__
- [RFC9229](https://www.rfc-editor.org/info/rfc9229): IPv4 Routes with an IPv6 Next Hop in the Babel Routing Protocol
- [RFC9268](https://www.rfc-editor.org/info/rfc9268): IPv6 Minimum Path MTU Hop-by-Hop Option
-
### [Chapter Contents](20.%20Further%20Reading.md)
diff --git a/utilities/RFCbib6.py b/utilities/RFCbib6.py
new file mode 100644
index 0000000..f4a4119
--- /dev/null
+++ b/utilities/RFCbib6.py
@@ -0,0 +1,235 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+"""Build IPv6 RFC bibliography"""
+
+# Version: 2023-05-21 - original
+
+
+########################################################
+# Copyright (C) 2023 Brian E. Carpenter.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with
+# or without modification, are permitted provided that the
+# following conditions are met:
+#
+# 1. Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDERS
+# 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 COPYRIGHT OWNER 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.
+#
+########################################################
+
+from tkinter import Tk
+from tkinter.filedialog import askdirectory
+from tkinter.messagebox import askokcancel, askyesno, showinfo
+
+import time
+import os
+
+def logit(msg):
+ """Add a message to the log file"""
+ global flog, printing
+ flog.write(msg+"\n")
+ if printing:
+ print(msg)
+
+def logitw(msg):
+ """Add a warning message to the log file"""
+ global warnings
+ logit("WARNING: "+msg)
+ warnings += 1
+
+def dprint(*msg):
+ """ Diagnostic print """
+ global printing
+ if printing:
+ print(*msg)
+
+def crash(msg):
+ """Log and crash"""
+ printing = True
+ logit("CRASH "+msg)
+ flog.close()
+ exit()
+
+def rf(f):
+ """Return a file as a list of strings"""
+ file = open(f, "r",encoding='utf-8', errors='replace')
+ l = file.readlines()
+ file.close()
+ #ensure last line has a newline
+ if l[-1][-1] != "\n":
+ l[-1] += "\n"
+ return l
+
+def wf(f,l):
+ """Write list of strings to file"""
+ file = open(f, "w",encoding='utf-8')
+ for line in l:
+ file.write(line+"\n")
+ file.close()
+ logit("'"+f+"' written")
+
+def field(fname, block):
+ """Extract named field from XML block"""
+ _,temp = block.split("<"+fname+">", maxsplit=1)
+ result,_ = temp.split(""+fname+">", maxsplit=1)
+ return result
+
+
+def title(block):
+ """Extract title from XML block"""
+ return field("title", block)
+
+def doc_id(block):
+ """Extract doc-id from XML block"""
+ return field("doc-id", block)
+
+
+def interesting(block):
+ """Save interesting RFC data from XML block"""
+ global stds, bcps, infos, exps
+
+ if "UNKNOWN" in block:
+ return None
+ elif "HISTORIC" in block:
+ return False
+ elif "" in block:
+ return False
+ elif "IPv6" in title(block):
+ #print(block)
+ status = field("current-status", block)
+ if "STANDARD" in status:
+ stds.append("- {{{"+doc_id(block)+"}}}: "+title(block))
+ elif status == "BEST CURRENT PRACTICE":
+ bcps.append("- {{{"+doc_id(block)+"}}}: "+title(block))
+ elif status =="INFORMATIONAL":
+ infos.append("- {{{"+doc_id(block)+"}}}: "+title(block))
+ elif status == "EXPERIMENTAL":
+ exps.append("- {{{"+doc_id(block)+"}}}: "+title(block))
+ else:
+ logitw("Unclassified:\n"+block)
+ return True
+ return False
+
+######### Startup
+
+#Define some globals
+
+inrfc = False
+new = ''
+numberfound = False
+count = 0
+stds = []
+bcps = []
+infos= []
+exps = []
+printing = False # True for extra diagnostic prints
+warnings = 0
+
+#Announce
+
+Tk().withdraw() # we don't want a full GUI
+
+T = "IPv6 RFC bibliography maker."
+
+printing = askyesno(title=T,
+ message = "Diagnostic printing?")
+
+where = askdirectory(title = "Select main book directory")
+
+os.chdir(where)
+
+#Open log file
+
+flog = open("RFCbib6.log", "w",encoding='utf-8')
+timestamp = time.strftime("%Y-%m-%d %H:%M:%S UTC%z",time.localtime())
+logit("RFCbib6 run at "+timestamp)
+
+logit("Running in directory "+ os.getcwd())
+
+
+showinfo(title=T,
+ message = "Will read complete RFC bibliography.\nTouch no files until done!")
+
+whole = rf("C:/brian/docs/IETF stuff/rfc/rfc-index.xml")
+timestamp = time.strftime("%Y-%m-%d %H:%M:%S UTC%z",time.localtime())
+
+for line in whole:
+ #print(line)
+ if (not inrfc) and (not "" in line):
+ continue
+ inrfc = True
+ new += line
+ if inrfc and "" in line:
+ #end of an rfc entry
+ if interesting(new):
+ count += 1
+ inrfc = False
+ numberfound = False
+ new = ''
+ continue
+
+logit(str(count)+" IPv6 RFCs found")
+if len(stds)+len(bcps)+len(infos)+len(exps) != count:
+ logitw("Warning: count mismatch")
+
+md = ["## RFC bibliography","",
+ """This section is a machine-generated list of all current RFCs that mention
+IPv6 in their title. Obsolete RFCs are not included. Be *cautious* about old
+Informational or Experimental RFCs - they may be misleading as well as out of date."""]
+md += ["","RFCbib6 run at "+timestamp]
+md += ["","__Standards Track__",""]
+md += stds
+md += ["", "__Best Current Practice__", ""]
+md += bcps
+md += ["", "__Informational__", ""]
+md += infos
+md += ["", "__Experimental__", ""]
+md += exps
+md += ["", "",
+ "### [Chapter Contents](20.%20Further%20Reading.md)"]
+
+wf("20. Further Reading/RFC bibliography.md", md)
+
+######### Close log and exit
+
+flog.close()
+
+if warnings:
+ warn = str(warnings)+" warning(s)\n"
+else:
+ warn = ""
+
+showinfo(title=T,
+ message = warn+"Check RFCbib6.log, then run makeBook.")
+
+
+