anchor.diff:  			[?'DIAMETER_BASE_RESULT-CODE_UNKNOWN_PEER']),
   	{answer_message, Error}.
   
  --spec process_request(ServiceName, Capabilities, Request,
  -		Address, Port, PasswordReq, Trusted) -> Result
  +-spec process_request(ServiceName, Capabilities, Request) -> Result
   	when
   		ServiceName :: term(),
   		Capabilities :: capabilities(),
   		Request :: term(),
  -		Address :: inet:ip_address(),
  -		Port :: inet:port(),
  -		PasswordReq :: boolean(),
  -		Trusted :: boolean(),
   		Result :: {reply, packet()} | discard.
   %% @doc Process a received DIAMETER packet.
   %% @private
   %% @todo Handle S6a/S6d requests.
  -process_request(ServiceName, _Capabilities, Request,
  -		Address, Port, PasswordReq, Trusted) ->
  -erlang:display({?MODULE, ?LINE, Request}), discard;
  -process_request(ServiceName, #diameter_caps{origin_host = {OHost, _DHost},
  +process_request(ServiceName,
  +		#diameter_caps{origin_host = {OHost, _DHost},
   		origin_realm = {ORealm, _DRealm}} = Capabilities,
  -		#'3gpp_s6a_AIR'{'Session-Id' = SId} = Request,
  -		Address, Port, PasswordReq, Trusted) ->
  +		#'3gpp_s6a_AIR'{'Session-Id' = SId} = Request) ->
  +erlang:display({?MODULE, ?LINE, Request}),
   	try
  -		process_request1(ServiceName, Capabilities,
  -				Request, Address, Port, PasswordReq, Trusted)
  +		authentication_information(ServiceName, Capabilities, Request)
   	catch
   		_:_Reason ->
   			{reply, #'3gpp_s6a_AIA'{'Session-Id' = SId,
   					'Result-Code' = ?'DIAMETER_BASE_RESULT-CODE_INVALID_AVP_BITS',
   					'Origin-Host' = OHost, 'Origin-Realm' = ORealm}}
  +	end;
  +process_request(ServiceName,
  +		#diameter_caps{origin_host = {OHost, _DHost},
  +		origin_realm = {ORealm, _DRealm}} = Capabilities,
  +		#'3gpp_s6a_ULR'{'Session-Id' = SId} = Request) ->
  +erlang:display({?MODULE, ?LINE, Request}),
  +	try
  +		update_location(ServiceName, Capabilities, Request)
  +	catch
  +		_:_Reason ->
  +			{reply, #'3gpp_s6a_ULA'{'Session-Id' = SId,
  +					'Result-Code' = ?'DIAMETER_BASE_RESULT-CODE_INVALID_AVP_BITS',
  +					'Origin-Host' = OHost, 'Origin-Realm' = ORealm}}
   	end.
  +
   %% @hidden
  -process_request1(ServiceName, Capabilities,
  -		Request, Address, Port, PasswordReq, Trusted) ->
  -	[Info] = diameter:service_info(ServiceName, transport),
  -	case lists:keyfind(options, 1, Info) of
  -		{options, Options} ->
  -			case lists:keyfind(transport_config, 1, Options) of
  -				{transport_config, TC} ->
  -					{ip, Sip} = lists:keyfind(ip, 1, TC),
  -					{port, Sport} = lists:keyfind(port, 1, TC),
  -					case global:whereis_name({ocs_diameter_auth, Sip, Sport}) of
  -						undefined ->
  -							discard;
  -						PortServer ->
  -							Answer = gen_server:call(PortServer,
  -									{diameter_request, Capabilities,
  -											Address, Port, PasswordReq, Trusted,
  -											Request, none}),
  -							{reply, Answer}
  +authentication_information(ServiceName,
  +		#diameter_caps{origin_host = {OHost, _DHost},
  +		origin_realm = {ORealm, _DRealm}} = _Capabilities,
  +		#'3gpp_s6a_AIR'{'Session-Id' = SId, 'User-Name' = IMSI,
  +		'Requested-EUTRAN-Authentication-Info' = ReqEutranAuth}) ->
  +	case ocs:find_service(IMSI) of
  +		{ok, #service{enabled = false}} ->
  +			{reply, #'3gpp_s6a_AIA'{'Session-Id' = SId,
  +					'Result-Code' = ?'DIAMETER_BASE_RESULT-CODE_AUTHORIZATION_REJECTED',
  +					'Origin-Host' = OHost, 'Origin-Realm' = ORealm}};
  +		{ok, #service{password = #aka_cred{k = K, opc = OPc, dif = DIF},
  +				attributes = _Attributes}} ->
  +			% @todo Handle two digit MNC!
  +			<<Mcc1, Mcc2, Mcc3, Mnc1, Mnc2, Mnc3, _/binary>> = IMSI,
  +			SN = <<Mcc2:4, Mcc1:4, Mnc3:4, Mcc3:4, Mnc2:4, Mnc1:4>>,
  +			AMF = <<0:16>>,
  +			case ReqEutranAuth of
  +				[#'3gpp_s6a_Requested-EUTRAN-Authentication-Info'{
  +						'Re-Synchronization-Info' = []}] ->
  +					RAND = ocs_milenage:f0(),
  +					{XRES, CK, IK, <<AK:48>>} = ocs_milenage:f2345(OPc, K, RAND),
  +					SQN = sqn(DIF),
  +					MAC = ocs_milenage:f1(OPc, K, RAND, <<SQN:48>>, AMF),
  +					AUTN = autn(SQN, AK, AMF, MAC),
  +					KASME = kdf(CK, IK, SN, SQN, AK),
  +					EutranVector = #'3gpp_s6a_E-UTRAN-Vector'{'Item-Number' = 1,
  +							'RAND' = RAND, 'XRES' = XRES, 'AUTN' = AUTN,
  +							'KASME' = KASME},
  +					AuthInfo = #'3gpp_s6a_Authentication-Info'{'E-UTRAN-Vector' = EutranVector},
  +					{reply, #'3gpp_s6a_AIA'{'Session-Id' = SId,
  +							'Result-Code' = ?'DIAMETER_BASE_RESULT-CODE_SUCCESS',
  +							'Origin-Host' = OHost, 'Origin-Realm' = ORealm,
  +							'Authentication-Info' = AuthInfo}};
  +				[#'3gpp_s6a_Requested-EUTRAN-Authentication-Info'{
  +						'Re-Synchronization-Info' = <<RAND:16/binary, SQN:48, MAC_S:8/binary>>}] ->
  +					{XRES, CK, IK, <<AK:48>>} = ocs_milenage:f2345(OPc, K, RAND),
  +					SQNhe = sqn(DIF),
  +					SQNms = sqn_ms(SQN, OPc, K, RAND),
  +					case SQNhe - SQNms of
  +						A when A =< 268435456 ->
  +							MAC_A = ocs_milenage:f1(OPc, K, RAND, <<SQNhe:48>>, AMF),
  +							AUTN = autn(SQNhe, AK, AMF, MAC_A),
  +							KASME = kdf(CK, IK, SN, SQNhe, AK),
  +							EutranVector = #'3gpp_s6a_E-UTRAN-Vector'{'Item-Number' = 1,
  +									'RAND' = RAND, 'XRES' = XRES, 'AUTN' = AUTN,
  +									'KASME' = KASME},
  +							AuthInfo = #'3gpp_s6a_Authentication-Info'{'E-UTRAN-Vector' = EutranVector},
  +							{reply, #'3gpp_s6a_AIA'{'Session-Id' = SId,
  +									'Result-Code' = ?'DIAMETER_BASE_RESULT-CODE_SUCCESS',
  +									'Origin-Host' = OHost, 'Origin-Realm' = ORealm,
  +									'Authentication-Info' = AuthInfo}};
  +						_ ->
  +							case ocs_milenage:'f1*'(OPc, K, RAND, <<SQNms:48>>, AMF) of
  +								MAC_S ->
  +									MAC_A = ocs_milenage:f1(OPc, K, RAND, <<SQNms:48>>, AMF),
  +									AUTN = autn(SQNms, AK, AMF, MAC_A),
  +									KASME = kdf(CK, IK, SN, SQNms, AK),
  +									save_dif(IMSI, dif(SQNms)),
  +									EutranVector = #'3gpp_s6a_E-UTRAN-Vector'{'Item-Number' = 1,
  +											'RAND' = RAND, 'XRES' = XRES, 'AUTN' = AUTN,
  +											'KASME' = KASME},
  +									AuthInfo = #'3gpp_s6a_Authentication-Info'{'E-UTRAN-Vector' = EutranVector},
  +									{reply, #'3gpp_s6a_AIA'{'Session-Id' = SId,
  +											'Result-Code' = ?'DIAMETER_BASE_RESULT-CODE_SUCCESS',
  +											'Origin-Host' = OHost, 'Origin-Realm' = ORealm,
  +											'Authentication-Info' = AuthInfo}};
  +								_ ->
  +									error_logger:error_report(["AUTS verification failed",
  +											{service, ServiceName}, {identity, IMSI},
  +											{auts, <<SQN:48, MAC_S/binary>>}]),
  +									{reply, #'3gpp_s6a_AIA'{'Session-Id' = SId,
  +											'Experimental-Result' = #'3gpp_s6a_Experimental-Result'{
  +											'Vendor-Id' = ?IANA_PEN_3GPP,
  +											'Experimental-Result-Code' = ?'DIAMETER_AUTHENTICATION_DATA_UNAVAILABLE'},
  +											'Origin-Host' = OHost, 'Origin-Realm' = ORealm}}
  +							end
   					end;
  -				false ->
  -					discard
  +				[] ->
  +					{reply, #'3gpp_s6a_AIA'{'Session-Id' = SId,
  +							'Result-Code' = ?'DIAMETER_BASE_RESULT-CODE_SUCCESS',
  +							'Origin-Host' = OHost, 'Origin-Realm' = ORealm}}
   			end;
  -		false ->
  -			discard
  +		{error, not_found} ->
  +			{reply, #'3gpp_s6a_AIA'{'Session-Id' = SId,
  +					'Experimental-Result' = #'3gpp_s6a_Experimental-Result'{
  +					'Vendor-Id' = ?IANA_PEN_3GPP,
  +					'Experimental-Result-Code' = ?'DIAMETER_ERROR_USER_UNKNOWN'},
  +					'Origin-Host' = OHost, 'Origin-Realm' = ORealm}};
  +		{error, Reason} ->
  +			error_logger:error_report(["Service lookup failure",
  +					{service, ServiceName}, {module, ?MODULE}, {error, Reason}]),
  +			{reply, #'3gpp_s6a_AIA'{'Session-Id' = SId,
  +					'Result-Code' = ?'DIAMETER_BASE_RESULT-CODE_UNABLE_TO_COMPLY',
  +					'Origin-Host' = OHost, 'Origin-Realm' = ORealm}}
   	end.
  +
  +%% @hidden
  +update_location(ServiceName, Capabilities, Request) ->
  +	discard.
   
   -spec errors(ServiceName, Capabilities, Request, Errors) -> Result
   	when
  
positive.code: errors(_ServiceName, _Capabilities, _Request, []) ->
  	ok.
  
  
negative.code: %% @hidden
  request(ServiceName, Capabilities, Request, [H | T]) ->
  	case ocs:find_client(H) of
  		{ok, #client{protocol = diameter}} ->
  			process_request(ServiceName, Capabilities, Request);
  		{error, not_found} ->
  			request(ServiceName, Capabilities, Request, T)
  	end;
  